﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Model_Layer;
using Data_Access_Layer;

namespace Business_Logic_Layer
{
    public class BLLDijkstraWIds
    {
        private static IDBFlight dbFlightObj = new DBFlight();
        private static IDBAirport dbAirportObj = new DBAirport();

        #region Dikstra's algorithm
        public List<Airport> RunDijkstra(Airport source, Airport target, string wClause)
        {
            try
            {
                List<Airport> verticeObjList = dbAirportObj.GetAllAirports();
                List<Flight> edgeObjList = dbFlightObj.GetCriteriaFlights(wClause, true);
                //List<Flight> edgeObjList = dbFlightObj.GetAllFlights(true);

                Airport u = source;
                List<Airport> dijkstraSolutionList = new List<Airport>();

                #region v's distance to infinity and previous to null 
                foreach (Airport v in verticeObjList)
                {
                    v.Dist = int.MaxValue;
                    v.PreviousVertex = null; // undefined
                }
                #endregion

                #region Assigning the dist on the element in the list which is equal to 'u' to 0.
                for (int i = 0; i < verticeObjList.Count - 1; i++)
                {
                    if (verticeObjList[i].IdAirport == u.IdAirport)
                    {
                        verticeObjList[i].Dist = 0;
                    }
                    //else
                    //{
                    //    verticeObjList[i].Dist = int.MaxValue;
                    //    verticeObjList[i].PreviousVertex = nullAirport;
                    //    //verticeObjList[i].FlightList = new List<Flight>();
                    //}
                }
                #endregion

                while (verticeObjList.Count > 0) // while queue is not empty 
                {
                    u = SmallestDistInQ(verticeObjList);
                    #region Test method for u.
                    //MessageBox.Show("Current u: " + u.Name + "\n" +
                    //    "Dist: " + u.Dist.ToString() + "\n" +
                    //    "Airport ID: " + u.IdAirport + "\n" +
                    //    "Previous Vertex: " + u.PreviousVertex.Name.ToString());
                    #endregion

                    verticeObjList.Remove(u);
                    dijkstraSolutionList.Add(u);

                    if (u.Dist == int.MaxValue)
                    {
                        break;
                    }

                    #region Relaxation step
                    int alt;
                    for (int i = 0; i < verticeObjList.Count; i++)
                    {
                        foreach (int id in GetAdjIds(u))
                        {
                            if (verticeObjList[i].IdAirport == id && id != u.IdAirport)
                            {
                                alt = u.Dist + DistBetween(u, verticeObjList[i], edgeObjList);
                                // if (verticeObjList[i].Dist < some specific maxDist && numPassengers < getNumPassenger[verticeObjList[i]])
                                if (alt < verticeObjList[i].Dist) // Doesn't this prevent cycles with the assumption that negative weights dont exist?
                                {
                                    verticeObjList[i].Dist = alt;
                                    verticeObjList[i].PreviousVertex = u;

                                    #region Test method for relaxation step
                                    //MessageBox.Show("Current v: " + verticeObjList[i].Name.ToString() + "\n" +
                                    //    "Dist: " + verticeObjList[i].Dist.ToString() + "\n" +
                                    //    "Airport ID: " + verticeObjList[i].IdAirport.ToString() + "\n" +
                                    //    "Previous vertex: " + verticeObjList[i].PreviousVertex.Name.ToString());
                                    #endregion
                                    //DecreaseKey(verticeObjList[i]);
                                }
                            }
                        }
                    }
                    #endregion
                }//end while
                dijkstraSolutionList = BacktrackDijkstra(dijkstraSolutionList, source, target);
                
                #region Test for total distance which is to be moved.
                int totalDist = 0;
                foreach (Airport a in dijkstraSolutionList)
                {
                    totalDist += a.Dist;
                }
                MessageBox.Show("Num of elements in solution list: " + dijkstraSolutionList.Count.ToString() + "\n" +
                    "Total traveltime: " + totalDist);
                #endregion

                #region Test method for the returned list.
                //foreach (Airport a in dijkstraSolutionList)
                //{
                //    MessageBox.Show("Vertex: " + a.Name.ToString() + "\n" + "Dist: " + a.Dist.ToString() + "\n" + "Airport ID: " + a.IdAirport.ToString());
                //    + "\n" + "Previous vertex:  " + a.PreviousVertex.Name.ToString());
                //}
                #endregion
                return dijkstraSolutionList;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region BacktrackDijkstra
        public List<Airport> BacktrackDijkstra(List<Airport> verticeObjList, Airport source, Airport target)
        {
            try
            {
                //MessageBox.Show("Airport: " + target.Name + "\n" + "Airport ID: " + target.IdAirport);
                
                List<Airport> solutionList1 = new List<Airport>();
                for (int i = 0; i < verticeObjList.Count; i++)
                {
                    if (verticeObjList[i].IdAirport == target.IdAirport) //verticeObjList[i] is the target
                    {
                        // target = verticeObjList[i];
                        // break;

                        //MessageBox.Show("Airport: " + verticeObjList[i].Name + "\n" + "Airport ID: " + verticeObjList[i].IdAirport);
                        while (verticeObjList[i].PreviousVertex != null)
                        {
                            solutionList1.Add(verticeObjList[i]);
                            verticeObjList[i] = verticeObjList[i].PreviousVertex;
                        }
                    }
                }
                return solutionList1;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region Methods used in Dijkstra's

        #region GetAdjIds
        public List<int> GetAdjIds(Airport u)
        {
            BLLAirport bllAirpObj = new BLLAirport();
            u.AdjacentcyIdList = bllAirpObj.GetAdjIdAirportList(u.IdAirport);
            return u.AdjacentcyIdList;
        }
        #endregion

        #region Dist()
        public int Dist(Airport v, Airport source)
        {
            int dist = v.Dist - source.Dist;
            return dist;
        }
        #endregion
        
        #region Vertex with smallest distance in Queue
        public Airport SmallestDistInQ(List<Airport> Queue)
        {
            Airport smallestDist = new Airport();
            smallestDist.Dist = int.MaxValue;
            foreach (Airport q in Queue)
            {
                if (q.Dist < smallestDist.Dist) 
                {
                    smallestDist = q;
                }
            }
            return smallestDist;

            #region Wastecode
            //Airport i = Queue[0];
            //for (int j = 1; j < Queue.Count - 1; j++) 
            //{
            //    if(i.Dist > Queue[j].Dist)
            //    {
            //        i = Queue[j];
            //    }
            //}
            //return i;
            #endregion
        }
        #endregion

        // Maybe not needed if handled otherwise.
        #region GetAdjacentVertices(u)
        private List<Airport> GetAdjacentVertices(Airport u)
        {
            BLLAirport bllAirportObj = new BLLAirport();
            u.AdjacentcyList = bllAirportObj.GetAdjAirportList(u.IdAirport);
            return u.AdjacentcyList;
        }
        #endregion

        #region Dist_Between(u,v)
        public int DistBetween(Airport u, Airport v, List<Flight> edgeObjList)
        {
            int dist = 0;
            for(int i = 0; i < edgeObjList.Count - 1; i++)
            {
                if(edgeObjList[i].IdStartAirport == u.IdAirport && edgeObjList[i].IdDestinationAirport == v.IdAirport)
                {
                    dist = edgeObjList[i].Traveltime;
                }
            }
            #region Previous dummycode
            // Need to figure out id of the specific flight by Flight.idstartairport...
            //foreach (Flight e in edgeObjList) 
            //{ 
            //    e.Traveltime = SELECT traveltime FROM Flight WHERE e.Idstartairport = u.IdAirport && e.Iddestinationairport = v.IdAirport; 
            //    edgeList.add(e);  
            //}

            //foreach (Flight e in edgeObjList) { dist += e.Weight } 
            //Looking in Flight, traveltime. Remember Vertex.Weight != DistBetween()
            //int dist = flightObj.Traveltime;
            #endregion
            return dist;
        }
        #endregion

        // Not implemented yet and maybe not needed yet.
        #region DecreaseKey(v) in Queue
        private void DecreaseKey(Airport v)
        {

        }
        #endregion
        
        #endregion
    }
}